package GPool

import (
	"context"
	"fmt"
	"log"
	"sync/atomic"
	"time"
)

// 日志记录器
var logger = log.Default()

// 载荷发生器的状态
const (
	STATUS_ORIGINAL uint32 = 0    // 原始状态
	STATUS_STARTING uint32 = 1    // 正在启动
	STATUS_STARTED  uint32 = 2    // 已启动
	STATUS_STOPPING uint32 = 3    // 正在停止
	STATUS_STOPPED  uint32 = 4    // 已停止
)

// Caller 调用器接口，为用户提供个性化支持
type Caller interface {
	BuildReq() RawReq             							   // 构建请求
	Call(req []byte, timeoutNS time.Duration) ([]byte, error)  // 调用载荷发生器
	CheckResp(rawReq RawReq, rawResp RawResp) *CallResult      // 检查响应
}

// Generator 载荷发生器接口
type Generator interface {
	Start() bool                   // 启动载荷发生器，启动成功返回true
	Stop() bool                    // 停止载荷发生器，停止成功返回true
	Status() uint32                // 获取状态
	CallCount() int64              // 获取调用计数，每次启动会重置该计数
}

type myGenerator struct {
	caller Caller                  // 调用器
	status uint32                  // 载荷发生器的状态
	timeoutNS time.Duration        // 响应超时时间，单位为: ns(纳秒)
	lps uint32                     // 每秒载荷量
	durationNS time.Duration       // 负载持续时间，单位为: ns(纳秒)
	resultCh chan *CallResult      // 负载的输出结果列表(调用结果通道)
	concurrency uint32             // 载荷并发量
	tickets GoTickets              // goroutine池，大小由concurrency决定
	ctx context.Context            // 上下文
	cancelFunc context.CancelFunc  // 取消函数
}

type CallResult struct {
	ID int64                       // 载荷编号
	Req RawReq                     // 原生请求
	Resp RawResp                   // 原生响应
	Code RetCode                   // 响应代码
	Msg string                	   // 结果成因简述
	Elapse time.Duration           // 耗时
}

// RawReq 原生请求
type RawReq struct {
	ID int64                       // 载荷编号
	Req []byte                     // 请求数据
}

// RawResp 原生响应
type RawResp struct {
	ID int64
	Resp []byte
	Err error                      // 载荷处理过程中的错误
	Elapse time.Duration           // 载荷处理耗时
}

func NewGenerator(caller Caller, timeoutNS time.Duration, lps uint32, durationNS time.Duration, resultCh chan *CallResult) (Generator, error) {
	gen := &myGenerator{
		caller:      caller,
		status:      STATUS_ORIGINAL,
		timeoutNS:   timeoutNS,
		lps:         lps,
		durationNS:  durationNS,
		resultCh:    resultCh,
	}
	return gen, nil
}

// -------------------- 票池 -------------------

// GoTickets goroutine票池的接口
type GoTickets interface {
	Take()                         // 获得一张票
	Return()                       // 归还一张票
	Active() bool                  // 票池是否被激活
	Total() uint32                 // 总的票数
	Remainder() uint32             // 剩余票数
}

type myGoTickets struct {
	total uint32                   // 票的总数
	ticketCh chan struct{}         // 票的容器
	active bool                    // 票池是否已被激活
}

func NewGoTickets(total uint32) (GoTickets, error) {
	gt := myGoTickets{}
	if !gt.init(total) {
		return nil, fmt.Errorf("goroutine ticket pool init failed (total = %d)\n", total)
	}
	return &gt, nil
}

func (gt *myGoTickets) init(total uint32) bool {
	if gt.active {
		return false
	}
	if total == 0 {
		return false
	}
	ch := make(chan struct{}, total)
	n := int(total)
	for i := 0; i < n; i++ {
		ch <- struct{}{}
	}
	gt.ticketCh = ch
	gt.total = total
	gt.active = true
	return true
}

func (gt *myGoTickets) Take() {
	<- gt.ticketCh
	atomic.AddUint32(&gt.total, -1)
}

func (gt *myGoTickets) Return() {
	gt.ticketCh <- struct{}{}
	atomic.AddUint32(&gt.total, 1)
}

func (gt *myGoTickets) Active() bool {
	return gt.active
}

func (gt *myGoTickets) Total() uint32 {
	return gt.total
}

func (gt *myGoTickets) Remainder() uint32 {
	return uint32(int(gt.total) - len(gt.ticketCh))
}








